#ifdef __aarch64__
    .text
    .align 5
    //.p2align 5,,15
    .global IndirectGemmFp32_8x8
#ifndef __APPLE__
    .type IndirectGemmFp32_8x8, %function
#endif

.macro INIT_BIAS
    // we could also use "movi v0.4s, 0" to initialize v0 by 0
    // but let us use less immediate numbers
    // as wzr(w31) is prefined as 0
    // wo do this though it seems unnecessary to initialize register v16 ~ v31
    ld1 v16.4s, [x3]
    mov v17.8b, v16.8b
    mov v18.8b, v16.8b
    mov v19.8b, v16.8b
    mov v20.8b, v16.8b
    mov v21.8b, v16.8b
    mov v22.8b, v16.8b
    mov v23.8b, v16.8b
    mov v24.8b, v16.8b
    mov v25.8b, v16.8b
    mov v26.8b, v16.8b
    mov v27.8b, v16.8b
    mov v28.8b, v16.8b
    mov v29.8b, v16.8b
    mov v30.8b, v16.8b
    mov v31.8b, v16.8b
.endm

// void IndirectGemmFp32_8x8(input, weight, output, bias, step, ic4, oc4, offset);
// x0: input, x1: weight, x2: output, x3: bias, x4: step, x5: ic4, x6: oc4, x7: offset
IndirectGemmFp32_8x8:
    // registers v8 ~ v15 must be preserved by a callee across subroutine calls, according to
    // https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#simd-and-floating-point-registers
    // r19 ~ r29 should be also preserved
    // whereas our coding style do not permit such amount of parameters
    sub sp, sp, #128
    // performance between storing 4 registers at the same time and seperatly storing them on in-order cores
    // is not tested yet
    st1 {v8.4s, v9.4s, v10.4s, v11.4s}, [sp], #64
    st1 {v12.4s, v13.4s, v14.4s, v15.4s}, [sp], #64

    LoopStart:

        mov x10, x4
        mov x12, x1

        LoopKw:

            mov x11, x0
            INIT_BIAS

            // load input for output 1-2
            ld1 {v0.4s, v1.4s}, [x2], #32
            // load weight
            ld1 {v8.4s, v9.4s, v10.4s, v11.4s}, [x12], #64
            // step for output 1-2
            fmla v16.4s, v8.4s, v0.s[0]
            fmla v17.4s, v9.4s, v0.s[0]
            fmla v18.4s, v8.4s, v1.s[0]
            fmla v19.4s, v9.4s, v1.s[0]
            // load input for output 3-4
            ld1 {v2.4s, v3.4s}, [x2], #32
            // another step for output 1-2
            fmla v16.4s, v10.4s, v0.s[1]
            fmla v17.4s, v11.4s, v0.s[1]
            fmla v18.4s, v10.4s, v1.s[1]
            fmla v19.4s, v11.4s, v1.s[1]
            // load input  for output 5-8
            // input cache should be refreshed after loading
            // ATTENTION: advance is prefered, but advancing too much may lead to invalid prefetching 
            ld1 {v4.4s, v5.4s, v6.4s, v7.4s}, [x12], #64
            prfm pldl1keep, [x12, #64]
            prfm pldl1keep, [x12, #128]
            // step for output 3-8
            fmla v20.4s, v8.4s, v2.s[0]
            fmla v21.4s, v9.4s, v2.s[0]
            fmla v22.4s, v8.4s, v3.s[0]
            fmla v23.4s, v9.4s, v3.s[0]

            subs x13, x5, #1
            beq LoopIcEnd

            LoopIc:
                fmla v24.4s, v8.4s, v4.s[0]
                fmla v25.4s, v9.4s, v4.s[0]
                fmla v26.4s, v8.4s, v5.s[0]
                fmla v27.4s, v9.4s, v5.s[0]
                fmla v28.4s, v8.4s, v6.s[0]
                fmla v29.4s, v9.4s, v6.s[0]
                fmla v30.4s, v8.4s, v7.s[0]
                fmla v31.4s, v9.4s, v7.s[0]
                // load weight
                ld1 {v12.4s, v13.4s, v14.4s, v15.4s}, [x2], #64
                prfm pldl1keep, [x2, #64]
                prfm pldl1keep, [x2, #128]
                // step for output 3-8
                fmla v20.4s, v10.4s, v2.s[1]
                fmla v21.4s, v11.4s, v2.s[1]
                fmla v22.4s, v10.4s, v3.s[1]
                fmla v23.4s, v11.4s, v3.s[1]
                fmla v24.4s, v10.4s, v4.s[1]
                fmla v25.4s, v11.4s, v4.s[1]
                fmla v26.4s, v10.4s, v5.s[1]
                fmla v27.4s, v11.4s, v5.s[1]
                fmla v28.4s, v10.4s, v6.s[1]
                fmla v29.4s, v11.4s, v6.s[1]
                fmla v30.4s, v10.4s, v7.s[1]
                fmla v31.4s, v11.4s, v7.s[1]
                // another step for output 1-8
                fmla v16.4s, v12.4s, v0.s[2]
                fmla v17.4s, v13.4s, v0.s[2]
                fmla v18.4s, v12.4s, v1.s[2]
                fmla v19.4s, v13.4s, v1.s[2]
                fmla v20.4s, v12.4s, v2.s[2]
                fmla v21.4s, v13.4s, v2.s[2]
                fmla v22.4s, v12.4s, v3.s[2]
                fmla v23.4s, v13.4s, v3.s[2]
                fmla v24.4s, v12.4s, v4.s[2]
                fmla v25.4s, v13.4s, v4.s[2]
                fmla v26.4s, v12.4s, v5.s[2]
                fmla v27.4s, v13.4s, v5.s[2]
                fmla v28.4s, v12.4s, v6.s[2]
                fmla v29.4s, v13.4s, v6.s[2]
                fmla v30.4s, v12.4s, v7.s[2]
                fmla v31.4s, v13.4s, v7.s[2]
                // load weight
                ld1 {v8.4s, v9.4s, v10.4s, v11.4s}, [x12], #64
                // another step for output 1-8
                fmla v16.4s, v14.4s, v0.s[3]
                fmla v17.4s, v15.4s, v0.s[3]
                fmla v18.4s, v14.4s, v1.s[3]
                fmla v19.4s, v15.4s, v1.s[3]
                fmla v20.4s, v14.4s, v2.s[3]
                fmla v21.4s, v15.4s, v2.s[3]
                fmla v22.4s, v14.4s, v3.s[3]
                fmla v23.4s, v15.4s, v3.s[3]
                // load input for output 1-4
                ld1 {v0.4s, v1.4s, v2.4s, v3.4s}, [x2], #64
                fmla v24.4s, v14.4s, v4.s[3]
                fmla v25.4s, v15.4s, v4.s[3]
                fmla v26.4s, v14.4s, v5.s[3]
                fmla v27.4s, v15.4s, v5.s[3]
                fmla v28.4s, v14.4s, v6.s[3]
                fmla v29.4s, v15.4s, v6.s[3]
                fmla v30.4s, v14.4s, v7.s[3]
                fmla v31.4s, v15.4s, v7.s[3]
                // load input  for output 5-8
                ld1 {v4.4s, v5.4s, v6.4s, v7.4s}, [x12], #64
                prfm pldl1keep, [x12, #64]
                prfm pldl1keep, [x12, #128]
                // step for output 1-8
                fmla v16.4s, v8.4s, v0.s[0]
                fmla v17.4s, v9.4s, v0.s[0]
                fmla v18.4s, v8.4s, v1.s[0]
                fmla v19.4s, v9.4s, v1.s[0]
                fmla v16.4s, v10.4s, v0.s[1]
                fmla v17.4s, v11.4s, v0.s[1]
                fmla v18.4s, v10.4s, v1.s[1]
                fmla v19.4s, v11.4s, v1.s[1]
                fmla v20.4s, v8.4s, v2.s[0]
                fmla v21.4s, v9.4s, v2.s[0]
                fmla v22.4s, v8.4s, v3.s[0]
                fmla v23.4s, v9.4s, v3.s[0]

                subs x13, x13, #1
                bne LoopIc

            LoopIcEnd:
                fmla v24.4s, v8.4s, v4.s[0]
                fmla v25.4s, v9.4s, v4.s[0]
                fmla v26.4s, v8.4s, v5.s[0]
                fmla v27.4s, v9.4s, v5.s[0]
                fmla v28.4s, v8.4s, v6.s[0]
                fmla v29.4s, v9.4s, v6.s[0]
                fmla v30.4s, v8.4s, v7.s[0]
                fmla v31.4s, v9.4s, v7.s[0]
                // load weight
                ld1 {v12.4s, v13.4s, v14.4s, v15.4s}, [x2], #64
                prfm pldl1keep, [x2, #64]
                prfm pldl1keep, [x2, #128]
                // step for output 3-8
                fmla v20.4s, v10.4s, v2.s[1]
                fmla v21.4s, v11.4s, v2.s[1]
                fmla v22.4s, v10.4s, v3.s[1]
                fmla v23.4s, v11.4s, v3.s[1]
                fmla v24.4s, v10.4s, v4.s[1]
                fmla v25.4s, v11.4s, v4.s[1]
                fmla v26.4s, v10.4s, v5.s[1]
                fmla v27.4s, v11.4s, v5.s[1]
                fmla v28.4s, v10.4s, v6.s[1]
                fmla v29.4s, v11.4s, v6.s[1]
                fmla v30.4s, v10.4s, v7.s[1]
                fmla v31.4s, v11.4s, v7.s[1]
                // another step for output 1-8
                fmla v16.4s, v12.4s, v0.s[2]
                fmla v17.4s, v13.4s, v0.s[2]
                fmla v18.4s, v12.4s, v1.s[2]
                fmla v19.4s, v13.4s, v1.s[2]
                fmla v20.4s, v12.4s, v2.s[2]
                fmla v21.4s, v13.4s, v2.s[2]
                fmla v22.4s, v12.4s, v3.s[2]
                fmla v23.4s, v13.4s, v3.s[2]
                fmla v24.4s, v12.4s, v4.s[2]
                fmla v25.4s, v13.4s, v4.s[2]
                fmla v26.4s, v12.4s, v5.s[2]
                fmla v27.4s, v13.4s, v5.s[2]
                fmla v28.4s, v12.4s, v6.s[2]
                fmla v29.4s, v13.4s, v6.s[2]
                fmla v30.4s, v12.4s, v7.s[2]
                fmla v31.4s, v13.4s, v7.s[2]
                // another step for output 1-8
                fmla v16.4s, v14.4s, v0.s[3]
                fmla v17.4s, v15.4s, v0.s[3]
                fmla v18.4s, v14.4s, v1.s[3]
                fmla v19.4s, v15.4s, v1.s[3]
                fmla v20.4s, v14.4s, v2.s[3]
                fmla v21.4s, v15.4s, v2.s[3]
                fmla v22.4s, v14.4s, v3.s[3]
                fmla v23.4s, v15.4s, v3.s[3]
                fmla v24.4s, v14.4s, v4.s[3]
                fmla v25.4s, v15.4s, v4.s[3]
                fmla v26.4s, v14.4s, v5.s[3]
                fmla v27.4s, v15.4s, v5.s[3]
                fmla v28.4s, v14.4s, v6.s[3]
                fmla v29.4s, v15.4s, v6.s[3]
                fmla v30.4s, v14.4s, v7.s[3]
                fmla v31.4s, v15.4s, v7.s[3]
                // prefetching is not prefered while writing results in spite of cache missings
                // you could try prfm pstl2strm
                // there are almost no benefits observed though
                st1 {v16.4s, v17.4s}, [x0], #32
                add x2, x2, x5
                st1 {v18.4s, v19.4s}, [x0], #32
                add x2, x2, x5
                st1 {v20.4s, v21.4s}, [x0], #32
                add x2, x2, x5
                st1 {v22.4s, v23.4s}, [x0], #32
                add x2, x2, x5
                st1 {v24.4s, v25.4s}, [x0], #32
                add x2, x2, x5
                st1 {v26.4s, v27.4s}, [x0], #32
                add x2, x2, x5
                st1 {v28.4s, v29.4s}, [x0], #32
                add x2, x2, x5
                st1 {v30.4s, v31.4s}, [x0], #32

            subs x10, x10, #1
            add x0, x0, #32
            bne LoopKw

        subs x6, x6, #1
        bne LoopStart

    // do not forget to reset context
    sub sp, sp, #128
    ld1 {v8.4s, v9.4s, v10.4s, v11.4s}, [sp], #64
    ld1 {v12.4s, v13.4s, v14.4s, v15.4s}, [sp], #64
    ret
#endif
